"""Flowable + 业务混合应用列表资源。

根据 Flowable 历史实例查询当前用户发起的流程，并可选回填业务数据。

GET /api/applications/flowable
参数:
  applicant_id: 必填, 对应流程的 startedBy (employee_id)
  page: 页码 (默认1)
  size: 每页数量 (默认20)
  status: active|completed (可选)
  process_definition_key: 指定流程定义 key 过滤 (可选)
  include_business: true|false (默认 true)

返回:
  code, message, data = {total, page, size, records: [...]} 每条记录包含流程 + 可选业务字段
"""
from flask import request
from flask_restful import Resource
from ..utils.helpers import success_response, error_response, handle_errors
from ..utils.flowable_client import FlowableClient
from ..models import LeaveApplication, PurchaseApplication
try:
    from server.config.config import Config  # type: ignore
except ModuleNotFoundError:
    from config.config import Config  # type: ignore

# 复用已有的业务映射逻辑: 简化为最小字段

def _map_leave(app: LeaveApplication):
    d = app.to_dict()
    return {
        'business_kind': 'leave',
        'id': d['id'],
        'applicant_id': d.get('applicant_id'),
        'status': d['status'],
        'apply_type': d.get('apply_type'),
        'leave_type': d.get('leave_type'),
        'leave_hours': d.get('leave_hours'),
        'project_id': d.get('project_id'),
    }

def _map_purchase(app: PurchaseApplication):
    d = app.to_dict()
    return {
        'business_kind': 'purchase',
        'id': d['id'],
        'applicant_id': d.get('applicant_id'),
        'status': d['status'],
        'purchase_amount': d.get('purchase_amount'),
        'project_id': d.get('project_id'),
    }

def _extract_initiator(business_key: str):
    """从 businessKey 中解析发起人 (格式 applicantId:applicationId)."""
    if not business_key:
        return None
    if ':' in business_key:
        return business_key.split(':', 1)[0]
    return None


class FlowableApplicationListResource(Resource):
    """混合查询资源"""

    def __init__(self):
        self.flowable_client = FlowableClient(
            Config.FLOWABLE_REST_URL,
            Config.FLOWABLE_REST_USER,
            Config.FLOWABLE_REST_PASSWORD
        )

    @handle_errors
    def get(self):
        applicant_id = request.args.get('applicant_id')
        if not applicant_id:
            return error_response('缺少参数 applicant_id', 400)
        page = int(request.args.get('page', 1))
        size = int(request.args.get('size', 20))
        status = request.args.get('status')  # active|completed
        pd_key = request.args.get('process_definition_key')
        include_business = request.args.get('include_business', 'true').lower() == 'true'
        # 自动构造 businessKeyLike，不接受前端传入，保证只按 applicant_id 查。
        business_key_like = f"{applicant_id}:%"

        start_offset = (page - 1) * size
        used_runtime = False
        instances = []
        total = 0

        # 策略：
        # 1. active / 未指定：调用 runtime 接口获取运行中实例（无法按变量过滤，使用 businessKey 前缀解析 initiator 过滤）
        # 2. completed 或 runtime 为空：使用 historic 接口按变量 applicantId 精准过滤（不依赖 startUserId）
        if status != 'completed':
            try:
                # 总是使用 POST 高级查询并带上 businessKeyLike
                runtime_resp = self.flowable_client.query_runtime_process_instances(
                    start=start_offset,
                    size=size,
                    process_definition_key=pd_key,
                    business_key_like=business_key_like
                )
                rt_data = runtime_resp.get('data', [])              
                print(f"运行时查询实例数量: {len(rt_data)}")
                instances = rt_data
                total = len(instances)
                used_runtime = True
            except Exception:
                used_runtime = False

        # 需要 completed 或 runtime 没结果：使用历史接口
        if status == 'completed' or (not instances):
            finished_filter = True if status == 'completed' else (False if status == 'active' else None)
            try:
                # historic 也使用 businessKeyLike + applicantId 变量双保险
                hist = self.flowable_client.query_historic_process_instances(
                    started_by=None,
                    start=start_offset,
                    size=size,
                    process_definition_key=pd_key,
                    finished=finished_filter,
                    business_key_like=business_key_like,
                    variables=[{"name": "applicantId", "value": applicant_id, "operation": "equals"}]
                )
                instances = hist.get('data', [])
                total = hist.get('total', len(instances))
            except Exception as e:
                return error_response(f'查询 Flowable 实例失败: {e}', 502)

        # 活动任务查询（仅针对未完成的）
        active_ids = [i['id'] for i in instances if i.get('endTime') is None]
        tasks_by_instance = {}
        if active_ids:
            try:
                tasks = self.flowable_client.query_tasks(active_ids)
                for t in tasks:
                    pid = t.get('processInstanceId')
                    tasks_by_instance.setdefault(pid, []).append({
                        'task_id': t.get('id'),
                        'name': t.get('name'),
                        'assignee': t.get('assignee'),
                        'created': t.get('createTime')
                    })
            except Exception as e:
                # 任务查询失败不阻塞主结果
                tasks_by_instance['__error'] = str(e)

        records = []
        # 业务回填：通过 businessKey 关联
        if include_business:
            # businessKey 可能是 applicantId:applicationId 或仅 applicationId
            bk_to_app_id = {}
            for inst in instances:
                bk = inst.get('businessKey')
                if not bk:
                    continue
                if ':' in bk:
                    _, app_id = bk.split(':', 1)
                else:
                    app_id = bk
                bk_to_app_id[bk] = app_id

            if bk_to_app_id:
                app_ids = list({v for v in bk_to_app_id.values()})
                leave_apps = LeaveApplication.query.filter(LeaveApplication.id.in_(app_ids)).all()
                purchase_apps = PurchaseApplication.query.filter(PurchaseApplication.id.in_(app_ids)).all()
                biz_map = {}
                # 为每个应用填充所有对应的 businessKey（正常情况下 1:1）
                for la in leave_apps:
                    for bk, aid in bk_to_app_id.items():
                        if aid == str(la.id):
                            biz_map[bk] = _map_leave(la)
                for pa in purchase_apps:
                    for bk, aid in bk_to_app_id.items():
                        if aid == str(pa.id):
                            biz_map[bk] = _map_purchase(pa)
            else:
                biz_map = {}
        else:
            biz_map = {}

        # 业务映射后做 applicant 过滤 (当 fallback 使用时需要)
        filtered_instances = []
        for inst in instances:
            pid = inst.get('id')
            business_key = inst.get('businessKey')
            completed = inst.get('endTime') is not None
            rec = {
                'process_instance_id': pid,
                'process_definition_key': inst.get('processDefinitionKey'),
                'business_key': business_key,
                'started_by': inst.get('startUserId'),  # 保留原字段以兼容旧前端
                'initiator_id': _extract_initiator(business_key),
                'start_time': inst.get('startTime'),
                'end_time': inst.get('endTime'),
                'completed': completed,
                'duration_in_millis': inst.get('durationInMillis'),
                'current_tasks': tasks_by_instance.get(pid, []),
            }
            if include_business and business_key and business_key in biz_map:
                rec['business'] = biz_map[business_key]
            filtered_instances.append(rec)

        # 历史查询已经按 started_by 过滤；若运行时查询，也已本地过滤。
        # 不再执行 fallback 宽范围查询与二次过滤。

        for rec in filtered_instances:
            records.append(rec)

        # total 若为 runtime 过滤后数量，需要重新计算（historic 已是精确值）
        if used_runtime:
            total = len(records)

            # Unified frontend format: items, total, page, page_size, pages
            page_size = size
            items = records
            pages = (total + page_size - 1) // page_size if page_size else 1
            return success_response({
                'items': items,
                'total': total,
                'page': page,
                'page_size': page_size,
                'pages': pages
            }, '获取 Flowable 实例成功')
